/*
 * "Hello World" example.
 *
 * This example prints 'Hello from Nios II' to the STDOUT stream. It runs on
 * the Nios II 'standard', 'full_featured', 'fast', and 'low_cost' example
 * designs. It runs with or without the MicroC/OS-II RTOS and requires a STDOUT
 * device in your system's hardware.
 * The memory footprint of this hosted application is ~69 kbytes by default
 * using the standard reference design.
 *
 * For a reduced footprint version of this template, and an explanation of how
 * to reduce the memory footprint for a given application, see the
 * "small_hello_world" template.
 *
 */

#include <stdio.h>
#include "system.h"

#include "altera_avalon_uart.h"
#include "sys/alt_dev.h"
#include <stdlib.h> // malloc, free
#include <string.h>
#include <stddef.h>
#include <unistd.h>  // usleep (unix standard?)
#include <fcntl.h>
#include "altera_avalon_pio_regs.h"


#define DDR3_IF_BASE 0x8000000

/******************************************************************
*  Function: MemTestDataBus
*
*  Purpose: Tests that the data bus is connected with no
*           stuck-at's, shorts, or open circuits.
*
******************************************************************/
static int MemTestDataBus(unsigned int address)
{
  unsigned int pattern;
  unsigned int ret_code = 0x0;

  /* Perform a walking 1's test at the given address. */
  for (pattern = 1; pattern != 0; pattern <<= 1)
  {
    /* Write the test pattern. */
    IOWR_32DIRECT(address, 0, pattern);

    /* Read it back (immediately is okay for this test). */
    if (IORD_32DIRECT(address, 0) != pattern)
    {
      ret_code = pattern;
      break;
    }
  }
  return ret_code;
}


/******************************************************************
*  Function: MemTestAddressBus
*
*  Purpose: Tests that the address bus is connected with no
*           stuck-at's, shorts, or open circuits.
*
******************************************************************/
static int MemTestAddressBus(unsigned int memory_base, unsigned int nBytes)
{
  unsigned int address_mask = (nBytes - 1);
  unsigned int offset;
  unsigned int test_offset;

  unsigned int pattern     = 0xAAAAAAAA;
  unsigned int antipattern  = 0x55555555;

  unsigned int ret_code = 0x0;

  /* Write the default pattern at each of the power-of-two offsets. */
  for (offset = sizeof(unsigned int); (offset & address_mask) != 0; offset <<= 1)
  {
    IOWR_32DIRECT(memory_base, offset, pattern);
  }

  /* Check for address bits stuck high. */
  test_offset = 0;
  IOWR_32DIRECT(memory_base, test_offset, antipattern);
  for (offset = sizeof(unsigned int); (offset & address_mask) != 0; offset <<= 1)
  {
     if (IORD_32DIRECT(memory_base, offset) != pattern)
     {
        ret_code = (memory_base+offset);
        break;
     }
  }

  /* Check for address bits stuck low or shorted. */
  IOWR_32DIRECT(memory_base, test_offset, pattern);
  for (test_offset = sizeof(unsigned int); (test_offset & address_mask) != 0; test_offset <<= 1)
  {
    if (!ret_code)
    {
      IOWR_32DIRECT(memory_base, test_offset, antipattern);
      for (offset = sizeof(unsigned int); (offset & address_mask) != 0; offset <<= 1)
      {
        if ((IORD_32DIRECT(memory_base, offset) != pattern) && (offset != test_offset))
        {
          ret_code = (memory_base + test_offset);
          break;
        }
      }
      IOWR_32DIRECT(memory_base, test_offset, pattern);
    }
  }

  return ret_code;
}


/******************************************************************
*  Function: MemTest8_16BitAccess
*
*  Purpose: Tests that the memory at the specified base address
*           can be read and written in both byte and half-word
*           modes.
*
******************************************************************/
static int MemTest8_16BitAccess(unsigned int memory_base)
{
  int ret_code = 0x0;

  /* Write 4 bytes */
  IOWR_8DIRECT(memory_base, 0, 0x0A);
  IOWR_8DIRECT(memory_base, 1, 0x05);
  IOWR_8DIRECT(memory_base, 2, 0xA0);
  IOWR_8DIRECT(memory_base, 3, 0x50);

  /* Read it back as one word */
  if(IORD_32DIRECT(memory_base, 0) != 0x50A0050A)
  {
    ret_code = memory_base;
  }

  /* Read it back as two half-words */
  if (!ret_code)
  {
    if ((IORD_16DIRECT(memory_base, 2) != 0x50A0) ||
        (IORD_16DIRECT(memory_base, 0) != 0x050A))
    {
      ret_code = memory_base;
    }
  }

  /* Read it back as 4 bytes */
  if (!ret_code)
  {
    if ((IORD_8DIRECT(memory_base, 3) != 0x50) ||
        (IORD_8DIRECT(memory_base, 2) != 0xA0) ||
        (IORD_8DIRECT(memory_base, 1) != 0x05) ||
        (IORD_8DIRECT(memory_base, 0) != 0x0A))
    {
    ret_code = memory_base;
    }
  }

  /* Write 2 half-words */
  if (!ret_code)
  {
    IOWR_16DIRECT(memory_base, 0, 0x50A0);
    IOWR_16DIRECT(memory_base, 2, 0x050A);

    /* Read it back as one word */
    if(IORD_32DIRECT(memory_base, 0) != 0x050A50A0)
    {
      ret_code = memory_base;
    }
  }

  /* Read it back as two half-words */
  if (!ret_code)
  {
    if ((IORD_16DIRECT(memory_base, 2) != 0x050A) ||
        (IORD_16DIRECT(memory_base, 0) != 0x50A0))
    {
      ret_code = memory_base;
    }
  }

  /* Read it back as 4 bytes */
  if (!ret_code)
  {
    if ((IORD_8DIRECT(memory_base, 3) != 0x05) ||
        (IORD_8DIRECT(memory_base, 2) != 0x0A) ||
        (IORD_8DIRECT(memory_base, 1) != 0x50) ||
        (IORD_8DIRECT(memory_base, 0) != 0xA0))
    {
      ret_code = memory_base;
    }
  }

  return(ret_code);
}


/******************************************************************
*  Function: MemTestDevice
*
*  Purpose: Tests that every bit in the memory device within the
*           specified address range can store both a '1' and a '0'.
*
******************************************************************/
static int MemTestDevice(unsigned int memory_base, unsigned int nBytes)
{
  unsigned int offset;
  unsigned int pattern;
  unsigned int antipattern;
  unsigned int ret_code = 0x0;

  /* Fill memory with a known pattern. */
  for (pattern = 1, offset = 0; offset < nBytes; pattern++, offset+=4)
  {
    IOWR_32DIRECT(memory_base, offset, pattern);
  }

  printf(" .");

  /* Check each location and invert it for the second pass. */
  for (pattern = 1, offset = 0; offset < nBytes; pattern++, offset+=4)
  {
    if (IORD_32DIRECT(memory_base, offset) != pattern)
    {
      ret_code = (memory_base + offset);
      break;
    }
    antipattern = ~pattern;
    IOWR_32DIRECT(memory_base, offset, antipattern);
  }

  printf(" .");

  /* Check each location for the inverted pattern and zero it. */
  for (pattern = 1, offset = 0; offset < nBytes; pattern++, offset+=4)
  {
    antipattern = ~pattern;
    if (IORD_32DIRECT(memory_base, offset) != antipattern)
    {
      ret_code = (memory_base + offset);
      break;
    }
    IOWR_32DIRECT(memory_base, offset, 0x0);
  }
  return ret_code;
}

/******************************************************************
*  Function: TestRam
*
*  Purpose: Performs a full-test on the RAM specified.  The tests
*           run are:
*             - MemTestDataBus
*             - MemTestAddressBus
*             - MemTest8_16BitAccess
*             - MemTestDevice
*
******************************************************************/
static int test_ddr3()
{

  int memory_base, memory_end, memory_size;
  int ret_code = 0x0;

  /* Find out what range of memory we are testing */
  //MemGetAddressRange(&memory_base, &memory_end);

  memory_base = DDR3_IF_BASE;
  memory_end =  DDR3_IF_BASE + 0x8000;

  memory_size = (memory_end - memory_base);
  ret_code = MemTestDataBus(memory_base);

  if (ret_code)
  {
	  printf(" -Data bus test failed at bit 0x%X", (int)ret_code);
	  return ret_code;
  }
  else
	  printf(" -Data bus test passed\n");


    ret_code  = MemTestAddressBus(memory_base, memory_size);
    if  (ret_code)
    {
      printf(" -Address bus test failed at address 0x%X", (int)ret_code);
      return ret_code;
    }
    else
      printf(" -Address bus test passed\n");


  /* Test byte and half-word access. */
    ret_code = MemTest8_16BitAccess(memory_base);
    if  (ret_code)
    {
      printf(" -Byte and half-word access test failed at address 0x%X", (int)ret_code);
      return ret_code;
    }
    else
      printf(" -Byte and half-word access test passed\n");

  /* Test that each bit in the device can store both 1 and 0. */
    printf(" -Testing each bit in memory device.");
    ret_code = MemTestDevice(memory_base, memory_size);
    if  (ret_code)
    {
      printf("  failed at address 0x%X", (int)ret_code);
    }
    else
      printf("  passed\n");


  if (!ret_code)
  {
    printf("Memory at 0x%X Okay\n", memory_base);
    happy_led();
  }
}
void happy_led()
{
	int i,j;
	for (i=0; i<8; i++)
	{
		IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE, 0xffffffff);
		for (j=0; j<200000; j++) j++;
		IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE, 0);
		for (j=0; j<200000; j++) j++;
	}
}

void test_uart()
{
	int	uart;
	int result;
	char szHello[] = "Hello from Nios II!\r\n";
	char szRead[1];
	// open uart
	uart = open(USB_UART_NAME, O_ACCMODE); // UART_NAME defined in system.h
	if(!uart){
		printf("failed to open uart\n");
		return;
	}
	// write uart
	if (write(uart, szHello, strlen(szHello)) != strlen(szHello)){
		printf("failed to write uart");

	}
	close(uart);
	return;
}


void test_led()
{
	int i,j;
	for (i=0; i<15; i++)
	{
		IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE, i);
		for (j=0; j<1000000; j++) j++;
	}
}

int main()
{

  printf("Hello from Nios II!\n");  //Test JTAG UART
  while (1)
  {
	  test_led();		//Test LEDs
	  test_uart();     	//Test USB UART (Please set BAUD RATE = 115200 on the host, user should see a "hello" message in the console.)
	  test_ddr3();		//If the memory test passes, user should see all LEDs flashing quickly.
	  //test_sd_card(); //User can use the C library provided by Terasic to test the sd card interface. Due to the copyright issue, ignore it here.
  }
  return 0;
}
